#include "dialog.h"
#include "ui_dialog.h"
#include <QString>
#include <QDebug>
#include <QDateTime>
#include <QPushButton>
#include "QSerialPortInfo" //串口端口信息访问
static bool isopen;
static QString sendtext;
static QByteArray readtext;


const uint16_t wCRCTalbeAbs[]={
0x0000,0xCC01,0xD801,0x1400,0xF001,0x3C00,0x2800,0xE401,
0xA001,0x6C00,0x7800,0xB401,0x5000,0x9C01,0x8801,0x4400};
uint16_t  user_uart_crc(uint8_t *pchMsg, uint16_t wDataLen)
{
    uint16_t wCRC = 0xFFFF;
    uint16_t i;
    uint8_t chChar;
    for(i=0;i<wDataLen;i++)
    {
        chChar=*pchMsg++;
        wCRC=wCRCTalbeAbs[(chChar^wCRC)&15]^(wCRC>>4);
        wCRC=wCRCTalbeAbs[((chChar>>4)^wCRC)&15]^(wCRC>>4);
    }
    return wCRC;
}

Dialog::Dialog(QWidget *parent) :
    QDialog(parent),
    ui(new Ui::Dialog)
{
    ui->setupUi(this);

    current_date_time =QDateTime::currentDateTime();
    //即使没有界面，我们只用代码也可以打开串口
    ui->pushButton_send->setEnabled(false);//将这个按钮设置为无法点击的false状态，即灰色
    isopen = false;//全局var isopen就是false因为当前uart状态是关闭的
    //qt4只能用SIGNAL与SLOT去执行connect，其他的更简单的方式见自己总结的文档
    //若uart对象有readyread信号，说明收到了uart数据，则利用槽函数将收到的数据显示到text控件
   connect(&mSerialport,SIGNAL(readyRead()),this,SLOT(receivedata()));
   //connect(&pushButton_test,SIGNAL(clicked()),this,SLOT(mytestslot()));//信号直接写clicked就行，不需要QPushButton::
   timer1 = startTimer(1000);
   sendcmdTimer = startTimer(1000);
}

void Dialog::mytestslot()
{
    qDebug()<<"test button is click";
}


void Dialog::sendCmd(uint8_t cmd)
{
    QByteArray sendbyteArr;
    sendbyteArr.resize(9);
    sendbyteArr[0]=0x55;
    sendbyteArr[1]=0xaa;
    sendbyteArr[2]=cmd;
    sendbyteArr[3]=0;
    sendbyteArr[4]=0;
    uint16_t crc;
    crc=user_uart_crc((uint8_t*)(sendbyteArr.data()+2),3);
    sendbyteArr[5]=crc>>8;
    sendbyteArr[6]=crc&0xff;
    sendbyteArr[7]=0x77;
    sendbyteArr[8]=0xee;
    if(-1==mSerialport.write(sendbyteArr))
    {
        qDebug()<<"write err";
    }
}

//多个定时器处理
void Dialog::timerEvent(QTimerEvent * ev)
{
    int validlen=0;
    int i=0;
    if(ev->timerId()==sendcmdTimer)
    {
        if(isopen)
        {
            sendCmd(0x99);
        }
    }
    if(ev->timerId()==timer1)  //刷新界面 定时器
    {
        clearTimeout++;
        QLabel *labarr[]={ui->labv1,ui->labv2,ui->labv3,ui->labv4,ui->labv5,ui->labv6,ui->labv7,ui->labv8,ui->labv9,ui->labv10,
                         ui->labv11,ui->labv12,ui->labv13,ui->labv14,ui->labv15,ui->labv16};
        QLabel *Templabarr[]={ui->labT1,ui->labT2,ui->labT3,ui->labT4};
        if(!isopen){
            return;
        }
        //更新界面
        if(clearTimeout>2)//清空界面
        {
            for(int i=0;i<16;i++)
            {
                labarr[i]->setText("--");
            }
            for(int i=0;i<4;i++)
            {
                Templabarr[i]->setText(QString("--"));
            }
            ui->labtotalVol->setText(QString("--"));
            ui->labcurr->setText(QString("--"));
            ui->labbatstatus->setText(QString("--"));
            ui->labcellnum->setText(QString("--"));
            ui->labtnum->setText(QString("--"));
        }else
        {
            for(int i=0;i<cmd99info.cellNum;i++)
            {
                labarr[i]->setText(QString("%1").arg(cmd99info.cellVol[i]));
            }
            for(int i=0;i<cmd99info.tempNum;i++)
            {
                Templabarr[i]->setText(QString("%1").arg(cmd99info.cellTemp[i]));
            }
            ui->labtotalVol->setText(QString("%1").arg(cmd99info.totalvol));
            ui->labcurr->setText(QString("%1").arg(cmd99info.curr));
            ui->labbatstatus->setText(QString("%1").arg(cmd99info.BatStatus));
            ui->labcellnum->setText(QString("%1").arg(cmd99info.cellNum));
            ui->labtnum->setText(QString("%1").arg(cmd99info.tempNum));
            ui->labtavg->setText(QString("%1").arg(cmd99info.AvgTemp));
        }
    }
    if(ev->timerId()==handleDataTime)//显示数据定时器
    {
        QByteArray validdata;
        //这里是给validdata分配空间  分配之后才能使用[]去赋值读取。否则就会报错
        validdata.resize(200);
        int i=0;
        int index=3;
        while(!myqueue.empty())//uart数据不为空
        {
            //队列 是uint8_t 类型数据  需要与uint8_t去比较
            if(myqueue.dequeue()==(uint8_t)0x55)
            {
                if(!myqueue.empty() && myqueue.dequeue()==(uint8_t)0xaa)
                {
                    while(!myqueue.empty())//uart数据不为空
                    {
                        validdata[i++]=myqueue.dequeue();//validdata 存的 cmd status
                    }
                    uint8_t cmd=validdata[0];
                    uint8_t rcvsttaus=validdata[1];
                    uint16_t rcvlen=((uint8_t)validdata[2]<<8)|(uint8_t)validdata[3];

                    uint16_t   rcvcrc=(uint8_t)validdata[3+rcvlen+1]<<8|(uint8_t)validdata[3+rcvlen+2];
                    uint16_t  calcrc=user_uart_crc((uint8_t*)validdata.data(),rcvlen+4);

                    if(rcvcrc==calcrc)
                    {
                        switch(cmd)
                        {
                        case 0x99:
                            index=3;
                            clearTimeout=0;
                            cmd99info.totalvol=((validdata[++index]<<8)|validdata[++index])/100.00;
                            cmd99info.curr=(validdata[++index]<<8)|validdata[++index];
                            cmd99info.soc=validdata[++index];
                            cmd99info.Tmos=validdata[++index]-40;
                            cmd99info.AvgTemp=validdata[++index]-40;
                            cmd99info.Tenv=validdata[++index]-40;
                            cmd99info.BatStatus=validdata[++index];
                            cmd99info.TotalDsg=(validdata[++index]<<24)|(validdata[++index]<<16)|(validdata[++index]<<8)|(validdata[++index]);
                            cmd99info.TotalChg=(validdata[++index]<<24)|(validdata[++index]<<16)|(validdata[++index]<<8)|(validdata[++index]);
                            cmd99info.PreDsgTime=(validdata[++index]<<8)|validdata[++index];
                            cmd99info.Cyccnt=(validdata[++index]<<8)|validdata[++index];
                            cmd99info.BmsGuzhang=(validdata[++index]<<8)|validdata[++index];
                            cmd99info.BmsGaojing=(validdata[++index]<<8)|validdata[++index];
                            cmd99info.mosStatus=validdata[++index];
                            cmd99info.BalanceBit=(validdata[++index]<<24)|(validdata[++index]<<16)|(validdata[++index]<<8)|(validdata[++index]);
                            cmd99info.OvBit=(validdata[++index]<<24)|(validdata[++index]<<16)|(validdata[++index]<<8)|(validdata[++index]);
                            cmd99info.UvBit=(validdata[++index]<<24)|(validdata[++index]<<16)|(validdata[++index]<<8)|(validdata[++index]);
                            cmd99info.RemCap=(validdata[++index]<<8)|validdata[++index];
                            cmd99info.SOH=validdata[++index];
                            cmd99info.cellNum=validdata[++index];
                            if(cmd99info.cellNum>=24)
                            {
                                qDebug()<<"cell num is wrong";
                            }
                            for(int i=0;i<cmd99info.cellNum;i++)
                            {
                                cmd99info.cellVol[i]=((uint8_t)validdata[++index]<<8)|(uint8_t)validdata[++index];
                            }
                            cmd99info.tempNum=validdata[++index];
                            for(int i=0;i<cmd99info.tempNum;i++)
                            {
                                cmd99info.cellTemp[i]=validdata[++index]-40;
                            }
                            cmd99info.desCap=(validdata[++index]<<24)|(validdata[++index]<<16)|(validdata[++index]<<8)|(validdata[++index]);
                            cmd99info.fullCap=(validdata[++index]<<24)|(validdata[++index]<<16)|(validdata[++index]<<8)|(validdata[++index]);
                            cmd99info.premos=validdata[++index];
                            cmd99info.heatmos=validdata[++index];
                            cmd99info.Theat=validdata[++index]-40;
                            cmd99info.DET=validdata[++index];
                            cmd99info.Relay=validdata[++index];
                            cmd99info.BatType=validdata[++index];
                            cmd99info.gain1=(validdata[++index]<<8)|validdata[++index];
                            cmd99info.offset1=validdata[++index];
                            cmd99info.gain2=(validdata[++index]<<8)|validdata[++index];
                            cmd99info.offset2=validdata[++index];
                            qDebug()<<"index="<<index;
                            break;
                        case 0x88:
                            break;
                        default:
                            break;
                        }
                    }else
                    {
                        qDebug()<<"crc fail";
                    }
                }
            }
        }
    }
}

Dialog::~Dialog()
{
    delete ui;
}


//这个槽函数也不需要自己connect出来，这个代码中是通过在界面直接右键->转到槽出来的
void Dialog::on_comboBox_serialport_currentIndexChanged(const QString &arg1)
{
    ui->label_serialport->setText(arg1);
}

//这种槽函数是直接在界面点击跳转过来的，则不用自己写connect函数去链接，就是open按钮按下的操作
void Dialog::on_pushButton_open_clicked()
{
    if(true == isopen)//如果当前的串口已经是打开的状态进行的操作--已经是打开的再按肯定就关闭了
    {
        mSerialport.close();
        isopen = false;
        ui->pushButton_open->setText("打开");
        ui->comboBox_date->setEnabled(true);
        ui->comboBox_parity->setEnabled(true);
        ui->comboBox_stop->setEnabled(true);
        ui->comboBox_serialport->setEnabled(true);
        ui->comboBox_baudrate->setEnabled(true);
    }else
    {
        getconfig();//如果当前uart是关闭的，则需要根据界面的uart特点配置串口，根据配置的选项去open串口
        if(true==mSerialport.open(QSerialPort::ReadWrite))
        {
            qDebug()<<"open ok";
        }//open的时候跟套接字一样，可读可写
        else
        {
            qDebug()<<"open fail";
            return;
        }
        isopen = true;
        ui->pushButton_send->setEnabled(true);
        ui->pushButton_open->setText("关闭");
        ui->comboBox_date->setEnabled(false);
        ui->comboBox_parity->setEnabled(false);
        ui->comboBox_stop->setEnabled(false);
        ui->comboBox_serialport->setEnabled(false);
        ui->comboBox_baudrate->setEnabled(false);
        handleDataTime = startTimer(500);
    }
}

void Dialog::printFrame(QByteArray& ba)
{
    QString str;
    for(int i=0; i<ba.size(); i++){
        str += QString("%1").arg((unsigned char)ba.at(i), 2, 16, QLatin1Char('0') ) + ' ';
    }

    qDebug() << str;
}

char  Dialog::XOR(QByteArray buf,uint8_t len)
{
    uint8_t  ans = 0;
    for(int i=0;i<len;i++)
    {
        ans ^= buf[i];
    }
    return ans;
}

void Dialog::receivedata()
{
    QByteArray rcvarray = mSerialport.readAll();
    if(rcvarray.size()==0)
    {
        qDebug()<<"no data to handle";
        return;
    }
    qDebug()<<"hard uart recv data";
    for(int i=0;i<rcvarray.size();i++)
    {
        myqueue.enqueue(rcvarray[i]);
    }
}

void Dialog::getconfig()
{
    //界面上所有显示的字 都是字符串，不能直接给uart的属性赋值，需要根据字符串，赋值响应的枚举值
    mSerialport.setPortName(ui->comboBox_serialport->currentText());
    mSerialport.setBaudRate(QSerialPort::BaudRate(ui->comboBox_baudrate->currentText().toUInt()));
    mSerialport.setParity(QSerialPort::NoParity);
    mSerialport.setDataBits(QSerialPort::Data8);
    mSerialport.setStopBits(QSerialPort::OneStop);
}
//这个槽函数也不需要自己connect出来，这个代码中是通过在界面直接右键->转到槽出来的
void Dialog::on_pushButton_send_clicked()
{
    if(-1==mSerialport.write("12 34"))
    {
        qDebug()<<"write err";
    }
}

void Dialog::on_pushButton_clicked()
{
    foreach(const QSerialPortInfo &info, QSerialPortInfo::availablePorts())
    {
        // 自动读取串口号添加到端口portBox中
        ui->comboBox_serialport->clear();
        QSerialPort serial;
        serial.setPort(info);
        if(serial.open(QIODevice::ReadWrite))
        {
            ui->comboBox_serialport->addItem(info.portName());
            serial.close();
        }
    }
}
